#ifndef __C_NGISMODEL_MODEL_ATTRIBUTE_H__
#define __C_NGISMODEL_MODEL_ATTRIBUTE_H__

#include "IModelAttribute.h"
#include <string>
#include <vector>
#include "../tinyxml2.h"
using namespace tinyxml2;

namespace NGIS
{
	namespace Model
	{
		class CModelAttribute : public IModelAttribute
		{
		public:
			CModelAttribute()
			{

			}

			~CModelAttribute()
			{

			}

			void split(std::string& s, std::string& delim,std::vector< std::string >* ret)  
			{  
				size_t last = 0;  
				size_t index=s.find_first_of(delim,last);  
				while (index!=std::string::npos)  
				{  
					ret->push_back(s.substr(last,index-last));  
					last=index+1;
					if (s[last] == ' ') last++;
					index=s.find_first_of(delim,last);  
				}  
				if (index-last>0)  
				{  
					ret->push_back(s.substr(last,index-last));  
				}  
			}

			bool loadFromXml(const XMLElement* pModelClassElement)
			{
				const XMLElement* attributeEle = pModelClassElement->FirstChildElement("AttributeSet");

				const XMLElement* categoriesEle = attributeEle->FirstChildElement("Categories");
				const XMLElement* localAttributesEle = attributeEle->FirstChildElement("LocalAttributes");

				for (const XMLElement* childEle = categoriesEle->FirstChildElement(); 
					childEle; childEle = childEle->NextSiblingElement())
				{
					ModelCategory modelCategory;
					modelCategory.principle = childEle->Attribute("principle");
					modelCategory.path = childEle->Attribute("path");
					mModelCategoryList.push_back(modelCategory);
				}

				for (const XMLElement* childEle = localAttributesEle->FirstChildElement(); 
					childEle; childEle = childEle->NextSiblingElement())
				{
					LocalAttribute localAttribute;
					localAttribute.localName = childEle->Attribute("localName");
					localAttribute.wikiUrl = childEle->Attribute("wiki");
					std::string localTypeStr = childEle->Attribute("local");
					ELocalizationType localType = String2LocalizationType(localTypeStr);
					localAttribute.localType = localType;
					const XMLElement* keywords_ele = childEle->FirstChildElement("Keywords");
					const XMLElement* abstract_ele = childEle->FirstChildElement("Abstract");
					std::string keywords_str = keywords_ele->GetText();
					localAttribute.abstractInfo = abstract_ele->GetText();
					std::string delimer = ";";
					std::vector<std::string> ret;
					split(keywords_str, delimer, &ret);
					for (int iKey=0; iKey<ret.size(); iKey++)
					{
						localAttribute.keywords.push_back(ret[iKey]);
					}
					mLocalAttributeList.push_back(localAttribute);
				}

				return true;
			}

			bool formatToXml(XMLElement* pModelClassElement)
			{
				XMLDocument* doc = pModelClassElement->GetDocument();
				XMLElement* attributeEle = doc->NewElement("AttributeSet");

				XMLElement* categoriesEle = doc->NewElement("Categories");
				XMLElement* localAttributesEle = doc->NewElement("LocalAttributes");

				for (int i=0; i<mModelCategoryList.size(); i++)
				{
					ModelCategory temp_cate = mModelCategoryList[i];
					XMLElement* temp_ele = doc->NewElement("Category");
					temp_ele->SetAttribute("principle", temp_cate.principle.c_str());
					temp_ele->SetAttribute("path", temp_cate.path.c_str());

					categoriesEle->LinkEndChild(temp_ele);
				}

				for (int i=0; i<mLocalAttributeList.size(); i++)
				{
					LocalAttribute temp_local = mLocalAttributeList[i];
					XMLElement* temp_ele = doc->NewElement("LocalAttribute");
					temp_ele->SetAttribute("local", LocalizationType2String(temp_local.localType).c_str());
					temp_ele->SetAttribute("localName", temp_local.localName.c_str());
					temp_ele->SetAttribute("wiki", temp_local.wikiUrl.c_str());

					XMLElement* keywordsEle = doc->NewElement("Keywords");
					XMLElement* abstractEle = doc->NewElement("Abstract");

					std::string keywords_str = "";
					for (int iKey=0; iKey<temp_local.keywords.size(); iKey++)
					{
						std::string temp_str = temp_local.keywords[iKey];
						keywords_str+=temp_str;
						if (iKey!=temp_local.keywords.size()-1)
							keywords_str +="; ";
					}
					keywordsEle->SetText(keywords_str.c_str());

					abstractEle->SetText(temp_local.abstractInfo.c_str());

					temp_ele->LinkEndChild(keywordsEle);
					temp_ele->LinkEndChild(abstractEle);

					localAttributesEle->LinkEndChild(temp_ele);
				}

				attributeEle->LinkEndChild(categoriesEle);
				attributeEle->LinkEndChild(localAttributesEle);

				pModelClassElement->LinkEndChild(attributeEle);
				return true;
			}
		public:
			virtual int getLocalAttributeCount() 
			{
				return mLocalAttributeList.size();
			}

			virtual bool getLocalAttribute(int idx, LocalAttribute& pLocalAttribute)
			{
				if (idx<0 || idx>=mLocalAttributeList.size()) return false;
				pLocalAttribute = mLocalAttributeList[idx];
				return true;
			}

			virtual bool getLocalAttribute(ELocalizationType pLocalType, LocalAttribute& pLocalAttribute)
			{
				for (int i=0; i<mLocalAttributeList.size(); i++)
				{
					if (mLocalAttributeList[i].localType == pLocalType)
					{
						pLocalAttribute = mLocalAttributeList[i];
						return true;
					}
				}
				return false;
			}

			virtual bool addLocalAttributeInfo(LocalAttribute& pLocalAttribute)
			{
				for (int i=0; i<mLocalAttributeList.size(); i++)
				{
					if (mLocalAttributeList[i].localType == pLocalAttribute.localType)
					{
						return false;
					}
				}
				mLocalAttributeList.push_back(pLocalAttribute);
				return true;
			}

			virtual bool removeLocalAttribute(int idx)
			{
				if (idx<0 || idx>=mLocalAttributeList.size()) return false;
				std::vector<LocalAttribute>::iterator it = mLocalAttributeList.begin();
				mLocalAttributeList.erase(it+idx);
				return true;
			}

			virtual bool removeLocalAttribute(LocalAttribute& pLocalAttribute)
			{
				std::vector<LocalAttribute>::iterator it = mLocalAttributeList.begin();
				std::vector<LocalAttribute>::iterator end = mLocalAttributeList.end();
				for(; it!=end; it++)
				{
					if (it->localType == pLocalAttribute.localType)
					{
						mLocalAttributeList.erase(it);
						return true;
					}
				}
				return false;
			}

			virtual bool updateLocalAttribute(int idx, LocalAttribute& pLocalAttribute)
			{
				if (idx<0 || idx>=mLocalAttributeList.size()) return false;
				mLocalAttributeList[idx] = pLocalAttribute;
				return true;
			}

			virtual bool updateLocalAttribute(ELocalizationType pLocalType, LocalAttribute& pLocalAttribute)
			{
				for (int i=0; i<mLocalAttributeList.size(); i++)
				{
					if (mLocalAttributeList[i].localType == pLocalType)
					{
						mLocalAttributeList[i] = pLocalAttribute;
						return true;
					}
				}
				return false;
			}

			//////////////////////////////////////////////////////////////////////////
			virtual int getCategoryCount()
			{
				return mModelCategoryList.size();
			}

			virtual bool getCategory(int idx, ModelCategory& pModelCategory)
			{
				if (idx<0 || idx>=mModelCategoryList.size()) return false;
				pModelCategory = mModelCategoryList[idx];
				return true;
			}

			virtual bool addCategoryInfo(ModelCategory& pCategoryInfo) 
			{
				std::vector<ModelCategory>::iterator it = mModelCategoryList.begin();
				std::vector<ModelCategory>::iterator end = mModelCategoryList.end();
				for(; it!=end; it++)
				{
					if (it->principle == pCategoryInfo.principle && it->path == pCategoryInfo.path)
					{
						return false;
					}
				}
				mModelCategoryList.push_back(pCategoryInfo);
				return true;
			}

			virtual bool removeCategory(int idx)
			{
				if (idx<0 || idx>=mModelCategoryList.size()) return false;
				std::vector<ModelCategory>::iterator it = mModelCategoryList.begin();
				mModelCategoryList.erase(it+idx);
				return true;
			}

			virtual bool removeCategory(ModelCategory& pCategoryInfo)
			{
				std::vector<ModelCategory>::iterator it = mModelCategoryList.begin();
				std::vector<ModelCategory>::iterator end = mModelCategoryList.end();
				for(; it!=end; it++)
				{
					if (it->principle == pCategoryInfo.principle && it->path == pCategoryInfo.path)
					{
						mModelCategoryList.erase(it);
						return true;
					}
				}
				return false;
			}

			virtual bool updateCategory(int idx, ModelCategory& pCategoryInfo)
			{
				if (idx<0 || idx>=mModelCategoryList.size()) return false;
				mModelCategoryList[idx] = pCategoryInfo;
				return true;
			}

			virtual bool compareOther(IModelAttribute* pAttribute, std::string& obj, std::string& name)
			{
				if (pAttribute->getCategoryCount() != mModelCategoryList.size())
				{
					obj = "Categories";
					name = "CategoryCount";
					return false;
				}
				for (int i=0; i<mModelCategoryList.size(); i++)
				{
					ModelCategory temp_cate1 = mModelCategoryList[i];
					ModelCategory temp_cate2;
					pAttribute->getCategory(i, temp_cate2);
					if (temp_cate1.compareOther(temp_cate2) == false)
					{
						obj = "Category";
						name = temp_cate1.principle + "<->" + temp_cate2.principle;
						return false;
					}
				}
				if (pAttribute->getLocalAttributeCount() != mLocalAttributeList.size())
				{
					obj = "LocalAttributes";
					name = "LocalAttributeCount";
					return false;
				}
				for (int i=0; i<mLocalAttributeList.size(); i++)
				{
					LocalAttribute temp_local1 = mLocalAttributeList[i];
					LocalAttribute temp_local2;
					pAttribute->getLocalAttribute(i, temp_local2);
					if (temp_local1.compareOther(temp_local2) == false)
					{
						obj = "Category";
						name = temp_local1.localType + "<->" + temp_local2.localType;
						return false;
					}
				}
				return true;
			}

		private:
			std::vector<LocalAttribute> mLocalAttributeList;
			std::vector<ModelCategory> mModelCategoryList;
		};
	}
}

#endif

